package org.game.obj;

import lombok.extern.slf4j.Slf4j;
import org.common.mongo.EntityType;
import org.common.mongo.MongoService;
import org.common.mongo.collection.RoleCollection;
import org.common.mongo.entity.BaseEntity;
import org.common.utils.SpringUtils;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * 角色的db数据
 */
@Slf4j
public class RoleData {

    private final MongoService mongoService;

    /**
     * 角色DB数据
     */
    private final RoleCollection roleCollection;

    /**
     * EntityType->EntityClass
     */
    private static final Map<Integer, Class<? extends BaseEntity>> entityTypeToEntityClass = new HashMap<>();
    /**
     * EntityClass->EntityType
     */
    private static final Map<Class<? extends BaseEntity>, Integer> entityClassToEntityType = new HashMap<>();

    /**
     * 注册entityType和entityClass的映射关系
     */
    @SuppressWarnings("unchecked")
    public static <T extends BaseEntity> void registerEntityTypeAndEntityClass(Set<Class<?>> classes) {
        for (Class<?> clz : classes) {
            if (BaseEntity.class.isAssignableFrom(clz)) {
                continue;
            }
            EntityType entityType = clz.getAnnotation(EntityType.class);
            if (entityType == null) {
                log.error("entityType is null. entityClass = {}", clz);
                return;
            }
            entityTypeToEntityClass.put(entityType.value().getType(), (Class<T>) clz);
            entityClassToEntityType.put((Class<T>) clz, entityType.value().getType());
        }
    }

    /**
     * 根据entityClass获取entityType
     */
    public int getEntityType(Class<? extends BaseEntity> entityClass) {
        return entityClassToEntityType.get(entityClass);
    }
    public <T extends BaseEntity> int getEntityType(T entity) {
        return entityClassToEntityType.get(entity.getClass());
    }

    /**
     * entity的最大Id
     * 用于做自增uid用
     */
    private final Map<Class<? extends BaseEntity>, Long> entityMaxUidMap = new HashMap<>();

    public RoleData(RoleCollection roleCollection) {
        this.mongoService = SpringUtils.getBean(MongoService.class);
        this.roleCollection = roleCollection;
        // 加载entity的最大uid
        loadEntityMaxUid();
    }

    /**
     * 从RoleCollection中加载entity的最大uid到entityMaxUidMap
     */
    private void loadEntityMaxUid() {
        roleCollection.getEntity().forEach((entityType, entityMap) -> {
            entityMap.forEach((uid, entity) -> {
                Long maxUid = Math.max(entityMaxUidMap.getOrDefault(entity.getClass(), 0L), uid);
                entityMaxUidMap.put(entity.getClass(), maxUid);
            });
        });
    }

    /**
     * 获取entity的最大uid
     */
    public <T extends BaseEntity> Long getMaxUid(T entity) {
        return getMaxUid(entity.getClass());
    }
    public Long getMaxUid(Class<? extends BaseEntity> clazz) {
        return entityMaxUidMap.getOrDefault(clazz, 0L);
    }

    /**
     * 根据Id获取对应地Entity
     */
    @SuppressWarnings("unchecked")
    public <T extends BaseEntity> T getEntity(Class<T> clazz, long uid) {
        int entityType = getEntityType(clazz);
        Map<Long, ? extends BaseEntity> objMap = roleCollection.getEntity().get(entityType);
        if (objMap == null) {
            return null;
        }
        return (T) objMap.get(uid);
    }

    /**
     * 获取该类型所有entity
     */
    @SuppressWarnings("unchecked")
    public <T extends BaseEntity> Collection<T> getEntityList(Class<T> clazz) {
        int entityType = getEntityType(clazz);
        Map<Long, ? extends BaseEntity> objMap = roleCollection.getEntity().get(entityType);
        if (CollectionUtils.isEmpty(objMap)) {
            return new ArrayList<>();
        }
        return (Collection<T>) objMap.values();
    }
    /**
     * 添加Entity
     */
    public <T extends BaseEntity> void addEntity(T entity) {
        Class<? extends BaseEntity> clazz = entity.getClass();
        int entityType = getEntityType(entity);
        Map<Long, BaseEntity> entityMap = roleCollection.getEntity().computeIfAbsent(entityType, k -> new HashMap<>());
        if (entity.getUid() <= 0) {
            // 未设定uid时使用递增id
            long maxUid = getMaxUid(entity);
            entity.setUid(maxUid);
        }
        entityMap.put(entity.getUid(), entity);
        mongoService.replaceCollection(roleCollection);
    }

    /**
     * 更新Entity
     */
    public <T extends BaseEntity> void updateEntity(T entity) {
        int entityType = getEntityType(entity);
        Map<Long, BaseEntity> entityMap = roleCollection.getEntity().get(entityType);
        if (entityMap == null) {
            return;
        }
        entityMap.put(entity.getUid(), entity);
        Criteria criteria = Criteria.where("_id").is(roleCollection.getId());
        Query query = Query.query(criteria);
        String path = String.format("entity.%d.%d", entityType, entity.getUid());
        Update update = new Update().set(path, entity);
        mongoService.updateCollection(roleCollection, query, update);
    }

    /**
     * 删除Entity
     */
    public <T extends BaseEntity> void removeEntity(Class<T> clazz, long uid) {
        int entityType = getEntityType(clazz);
        Map<Long, ? extends BaseEntity> objMap = roleCollection.getEntity().get(entityType);
        if (objMap == null) {
            return;
        }
        objMap.remove(uid);
        Criteria criteria = Criteria.where("_id").is(roleCollection.getId());
        Query query = Query.query(criteria);
        String path = String.format("entity.%d.%d", entityType, uid);
        Update update = new Update().unset(path);
        mongoService.updateCollection(roleCollection, query, update);

    }

}
